



use crate::resp::{BulkString, RespArray, RespEncode, RespMap, RespNull, RespNullArray, RespNullBulkString, RespSet, SimpleError, SimpleString};


impl RespEncode for SimpleString {
    fn encode(self) -> Vec<u8> {
        format!("+{}\r\n", self.0).into_bytes()
    }
}

impl RespEncode for SimpleError {
    fn encode(self) -> Vec<u8> {
        format!("-{}\r\n", self.0).into_bytes()
    }
}

impl RespEncode for BulkString {
    fn encode(self) -> Vec<u8> {
        let mut buf = Vec::with_capacity(self.len() + 16);
        buf.extend_from_slice(&format!("${}\r\n", self.len()).into_bytes());
        buf.extend_from_slice(&self);
        buf.extend_from_slice(b"\r\n");
        buf
    }
}

impl RespEncode for RespNullBulkString {
    fn encode(self) -> Vec<u8> {
        b"$-1\r\n".to_vec()
    }
}

impl RespEncode for RespNull {
    fn encode(self) -> Vec<u8> {
        b"_\r\n".to_vec()
    }
}

const BUF_CAP: usize = 4096;
impl RespEncode for RespArray {
    fn encode(self) -> Vec<u8> {

        let mut buf = Vec::with_capacity(BUF_CAP);
        buf.extend_from_slice(&format!("*{}\r\n", self.len()).into_bytes());
        for item in self.0 {
            buf.extend_from_slice(&item.encode());
        }
        buf
    }
}

impl RespEncode for bool {
    fn encode(self) -> Vec<u8> {
        if self {
            b"+t\r\n".to_vec()
        } else {
            b"+f\r\n".to_vec()
        }
    }
    
}

impl RespEncode for f64 {
    fn encode(self) -> Vec<u8> {
        let mut buf = Vec::with_capacity(32);
        let ret = if self.abs() > 1e+8 {
            format!(",{:+e}\r\n", self)
        } else {
            let sign = if self < 0.0 { "-" } else { "" };
            format!(",{}{:.7}\r\n", sign, self)
        };
        buf.extend_from_slice(&ret.into_bytes());
        buf
    }
}

impl RespEncode for RespMap{
    fn encode(self) -> Vec<u8> {
        let mut buf = Vec::with_capacity(BUF_CAP);
        buf.extend_from_slice(&format!("%{}\r\n", self.0.len()).into_bytes());
        for (key, value) in self.0 {
            buf.extend_from_slice(&BulkString(key.into_bytes()).encode());
            buf.extend_from_slice(&value.encode());
        }
        buf
    }
}

impl RespEncode for RespSet {
    fn encode(self) -> Vec<u8> {
        let mut buf = Vec::with_capacity(BUF_CAP);
        buf.extend_from_slice(&format!("~{}\r\n", self.0.len()).into_bytes());
        for item in self.0 {
            buf.extend_from_slice(&item.encode());
        }
        buf
    }
    
}

impl RespEncode for i64 {
    fn encode(self) -> Vec<u8> {
        let sign = if self < 0 { "" } else { "+" };
        format!(":{}{}\r\n",sign,self).into_bytes()
    }
}

impl RespEncode for RespNullArray {
    fn encode(self) -> Vec<u8> {
        b"*-1\r\n".to_vec()
    }
    
}


#[cfg(test)]
mod test{
    use crate::resp::RespFrame;

    use super::*;

    #[test]
    fn test_resp_frame_encoding() {
        let frame: RespFrame = SimpleError::new("ERR unknown command 'foobar'".to_string()).into();
        assert_eq!(frame.encode(), b"-ERR unknown command 'foobar'\r\n");
    }

    #[test]
    fn test_resp_frame_encoding_bulk_string() {
        let frame: RespFrame = BulkString::new("Hello, world!".as_bytes().to_vec()).into();
        assert_eq!(frame.encode(), b"$13\r\nHello, world!\r\n");
    }
    #[test]
    fn test_integer_encoding() {
        let frame: RespFrame = 42.into();
        assert_eq!(frame.encode(), b":+42\r\n");
    }
}